use anyhow::anyhow;
use bytes::BufMut;

pub const SUPPORT_AUTHS: [SocksAuthMethod; 2] = [SocksAuthMethod::NoAuth, SocksAuthMethod::UserPwd];
pub const SOCKS_VERSION: u8 = 0x05;

//0x03 - 0x7F 由IANA分配
//0x80 - 0xFE 为个人保留方法
#[derive(Debug, PartialEq, Clone, Copy, PartialOrd, Eq, Ord)]
pub(crate) enum SocksAuthMethod {
    NoAuth = 0x00,
    Gssapi = 0x01,
    UserPwd = 0x02,
    Reject = 0xFF,
}

impl From<u8> for SocksAuthMethod {
    fn from(value: u8) -> Self {
        match value {
            0x00 => SocksAuthMethod::NoAuth,
            0x01 => Self::Gssapi,
            0x02 => Self::UserPwd,
            0xFF => Self::Reject,
            _ => Self::Reject,
        }
    }
}

impl From<SocksAuthMethod> for u8 {
    fn from(value: SocksAuthMethod) -> Self {
        match value {
            SocksAuthMethod::NoAuth => 0x00,
            SocksAuthMethod::Gssapi => 0x01,
            SocksAuthMethod::UserPwd => 0x02,
            SocksAuthMethod::Reject => 0xFF,
        }
    }
}

#[derive(Debug)]
pub(crate) enum SocksRequestCommand {
    Connect,
    Bind,
    Udp,
}
impl TryFrom<u8> for SocksRequestCommand {
    type Error = anyhow::Error;
    fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
        match value {
            0x01 => Ok(Self::Connect),
            0x02 => Ok(Self::Bind),
            0x03 => Ok(Self::Udp),
            _ => Err(anyhow!("unknown cmd")),
        }
    }
}

#[derive(Debug)]
pub(crate) struct SocksResponse {
    ver: u8,
    rep: SocksResponseType,
    rsv: u8, //保留字段
    atyp: SocksResponseAddressType,
    dst_addr: Vec<u8>,
    dst_port: u16,
}
pub(crate) struct SocksResponseBuilder {
    rep: SocksResponseType,
    atyp: SocksResponseAddressType,
    dst_addr: Vec<u8>,
    dst_port: u16,
}

impl SocksResponseBuilder {
    pub(crate) fn rep(&mut self, rep: SocksResponseType) -> &mut SocksResponseBuilder {
        self.rep = rep;
        self
    }
    pub(crate) fn atyp(&mut self, atyp: SocksResponseAddressType) -> &mut SocksResponseBuilder {
        self.atyp = atyp;
        self
    }
    pub(crate) fn dst_addr(&mut self, addr: Vec<u8>) -> &mut SocksResponseBuilder {
        self.dst_addr = addr;
        self
    }
    pub(crate) fn dst_port(&mut self, port: u16) -> &mut SocksResponseBuilder {
        self.dst_port = port;
        self
    }
    pub(crate) fn build(self) -> SocksResponse {
        SocksResponse {
            ver: SOCKS_VERSION,
            rep: self.rep,
            rsv: 0x00,
            atyp: self.atyp,
            dst_addr: self.dst_addr,
            dst_port: self.dst_port,
        }
    }
}

impl SocksResponse {
    pub(crate) fn builder() -> SocksResponseBuilder {
        SocksResponseBuilder {
            rep: SocksResponseType::ServerError,
            atyp: SocksResponseAddressType::Ipv4,
            dst_addr: vec![],
            dst_port: 0x00,
        }
    }
    pub(crate) fn rep(&self) -> SocksResponseType {
        self.rep
    }
    pub(crate) fn to_bytes(&self) -> Vec<u8> {
        let mut res = vec![];
        res.put_u8(self.ver);
        res.put_u8(self.rep.into());
        res.put_u8(self.rsv);
        res.put_u8(self.atyp.into());
        res.put(&self.dst_addr[..]);
        res.put_u16(self.dst_port);
        res
    }
}
/* REP是响应字段：
0x00：成功
0x01：服务器错误
0x02：规则禁止
0x03：网络不可达
0x04：主机不可达
0x05：连接被拒
0x06： TTL超时
0x07：不支持的命令
0x08：不支持的地址类型
0x09 - 0xFF：尚未定义 */
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum SocksResponseType {
    Success,
    ServerError,
    RuleProhibits,
    NetWorkInAccessible,
    HostInAccessible,
    ConnectReject,
    TTLTimeout,
    NoSupportCommand,
    NoSupportAddressType,
    AddressIncorrect,
}

impl From<SocksResponseType> for u8 {
    fn from(value: SocksResponseType) -> Self {
        match value {
            SocksResponseType::Success => 0x00,
            SocksResponseType::ServerError => 0x01,
            SocksResponseType::RuleProhibits => 0x02,
            SocksResponseType::NetWorkInAccessible => 0x03,
            SocksResponseType::HostInAccessible => 0x04,
            SocksResponseType::ConnectReject => 0x05,
            SocksResponseType::TTLTimeout => 0x06,
            SocksResponseType::NoSupportCommand => 0x07,
            SocksResponseType::NoSupportAddressType => 0x08,
            SocksResponseType::AddressIncorrect => 0x09,
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum SocksResponseAddressType {
    Ipv4,
    Domain,
    Ipv6,
}

impl From<SocksResponseAddressType> for u8 {
    fn from(value: SocksResponseAddressType) -> Self {
        match value {
            SocksResponseAddressType::Ipv4 => 0x01,
            SocksResponseAddressType::Domain => 0x03,
            SocksResponseAddressType::Ipv6 => 0x04,
        }
    }
}